﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace ReadCSV
{

    /*
     * LOP DOI TUONG THUOC TINH
     */ 
    public class DoiTuong
    {
        string _TenThuocTinh;
        string _LoaiThuocTinh;
        List<string> _DanhSach;        
        int _KichThuoc;
        string _Min;
        string _Max;
        string _GiaTri;
        long _SoMauPhanBiet;
        long _SoMauThieu;
        List<string> _DanhSachPhanBiet;
        List<int> _DemPhanBiet;
        string _mean;
        string _StdDev;

        /*
         * CAC HAM SET/GET
         */

        public string TenThuocTinh
        {
            get { return _TenThuocTinh; }
            set { _TenThuocTinh = value; }
        }
        public string LoaiThuocTinh
        {
            get { return _LoaiThuocTinh; }
            set { _LoaiThuocTinh = value; }
        }
        public List<string> DanhSach
        {
            get { return _DanhSach; }
            set { _DanhSach = value; }
        }      
        public string Min
        {
            get { return _Min; }            
        }
        public string Max
        {
            get { return _Max; }
        }
        public int KichThuoc
        {
            get { return _KichThuoc; }
        }
        public string GiaTri
        {
            get { return _GiaTri; }            
        }
        public List<int> DemPhanBiet
        {
            get { return _DemPhanBiet; }
        }
        public long SoMauPhanBiet
        {
            get { return _SoMauPhanBiet; }
        }
        public long SoMauThieu
        {
            get { return _SoMauThieu; }
        }
        public List<string> DanhSachMauPhanBiet
        {
            get { return _DanhSachPhanBiet; }
        }
        public string mean
        {
            get { return _mean; }            
        }
        public string StdDev
        {
            get { return _StdDev; }            
        }

        /*
         * CAC HAM 
         */

        public void Add(string temp)
        {
            if (_DanhSach == null)
                _DanhSach = new List<string>();
            _DanhSach.Add(temp);
            _KichThuoc = _DanhSach.Count;
        }

        string GTMin()
        {
            string temp = null;
            if (_LoaiThuocTinh == "numeric")
            {                
                double doumin = Convert.ToDouble(_DanhSach[0]);
                for (int i = 0; i < _KichThuoc; i++)
                {
                    if (_DanhSach[i] != "?")
                    {
                        double fg = Convert.ToDouble(_DanhSach[i]);
                        if (fg < doumin)
                            doumin = fg;
                    }
                }
                temp = doumin.ToString();
            }
            return temp;
        }

        string GTMax()
        {
            string temp = null;
            if (_LoaiThuocTinh == "numeric")
            {
                double doumin = Convert.ToDouble(_DanhSach[0]);
                for (int i = 0; i < _KichThuoc; i++)
                {
                    if (_DanhSach[i] != "?")
                    {
                        double fg = Convert.ToDouble(_DanhSach[i]);
                        if (fg > doumin)
                            doumin = fg;
                    }
                }
                temp = doumin.ToString();
            }
            return temp;
        }

        int TanSuat(string gt,List<string> danhsach)
        {
            int dem = 0;
            for (int i = 0; i < danhsach.Count; i++)
            {
                if (danhsach[i] == gt)
                    dem++;
            }
            return dem;
        }

        int TanSuat(int bd, string gt, List<string> danhsach)
        {
            int dem = 0;
            for (int i = bd; i < _KichThuoc; i++)
            {
                if (_DanhSach[i] == gt)
                    dem++;
            }
            return dem;
        }

        string GT_DauTien()
        {
            string temp = "";
            for (int i = 0; i < _KichThuoc; i++)
            {
                if (_DanhSach[i] != "?")
                    return _DanhSach[i];
            }
            return temp;
        }

        string TanSuat_Max()
        {
            string temp = GT_DauTien();
            int ts = TanSuat(temp,_DanhSach);
            for (int i = 0; i < _KichThuoc; i++)
            {
                if (DanhSach[i] != "?")
                {
                    int ts_n = TanSuat(i, _DanhSach[i], _DanhSach);

                    if (ts_n > ts)
                    {
                        temp = _DanhSach[i];
                        ts = ts_n;
                    }
                }
            }
            return temp;
        }

        string TrungBinh()
        {            
            if (_LoaiThuocTinh == "numeric")
            {                
                double dou = 0;
                for (int i = 0; i < _KichThuoc; i++)
                {
                    if (_DanhSach[i] != "?")
                        dou += Convert.ToDouble(_DanhSach[i]);
                }
                return  ((dou / (_KichThuoc - _SoMauThieu))).ToString();
            }
         
            return null;
        }

        string GiaTriDaiDien()
        {
            if (_LoaiThuocTinh == "nominal")
                return TanSuat_Max();
            return TrungBinh();
        }

        Boolean KiemTraGiaTriPhanBiet(string temp, List<string> danhsach)
        {
            if (danhsach == null)
                return true;
            for (int i = 0; i < danhsach.Count; i++)
            {
                if (temp == danhsach[i])
                    return false;
            }
            return true;
        }

        List<string> MauPhanBiet()
        {
            List<string> danhsach = new List<string>();
            for (int i = 0; i < _KichThuoc; i++)
            {
                if (_DanhSach[i] != "?")
                {
                    if (KiemTraGiaTriPhanBiet(_DanhSach[i], danhsach))
                    {
                        danhsach.Add(_DanhSach[i]);
                    }
                }
            }
            return danhsach;
        }

        public List<string> MauPhanBiet(List<string> ds_in)
        {
            List<string> danhsach = new List<string>();
            for (int i = 0; i < ds_in.Count; i++)
            {
                if (ds_in[i] != "?")
                {
                    if (KiemTraGiaTriPhanBiet(ds_in[i], danhsach))
                    {
                        danhsach.Add(ds_in[i]);
                    }
                }
            }
            return danhsach;
        }

        long MauThieu()
        {
            long MauThieu = 0;            
            for (int i = 0; i < _KichThuoc; i++)
            {
                if (_DanhSach[i] == "?")
                {
                    MauThieu++;
                }                
            }
            return MauThieu;
        }

        void SortDanhSach()
        {
            if (_LoaiThuocTinh == "numeric")
            {
                List<string> _DanhSachSort = _DanhSachPhanBiet;
                for (int i = 0; i < _DanhSachSort.Count - 1; i++)
                {
                    for (int j = i + 1; j < _DanhSachSort.Count; j++)
                    {
                        double a = Convert.ToDouble(_DanhSachSort[i]);
                        double b = Convert.ToDouble(_DanhSachSort[j]);
                        if (a > b)
                        {
                            string t = _DanhSachSort[i];
                            _DanhSachSort[i] = _DanhSachSort[j];
                            _DanhSachSort[j] = t;
                        }
                    }
                }
            }                        
        }

        void DemGiaTriPhanBiet()
        {
            int n = _DanhSachPhanBiet.Count;
            _DemPhanBiet = new List<int>();
            for (int i = 0; i < n; i++)
            {
                _DemPhanBiet.Add(TanSuat(_DanhSachPhanBiet[i], _DanhSach));
            }
        }

        public List<int> DemGiaTriPhanBiet(List<string> dspb,List<string> dsmau)
        {
            int n = dspb.Count;
            List<int> dem_ds = new List<int>();
            for (int i = 0; i < n; i++)
            {
                dem_ds.Add(TanSuat(dspb[i], dsmau));
            }
            return dem_ds;
        }

        string S_mean()
        {
            if (_LoaiThuocTinh == "numeric")
            {
                return _GiaTri;
            }
            return null;
        }

        string S_StdDev()
        {
            if (_LoaiThuocTinh == "numeric")
            {
                double temp = 0;

                double mean = Convert.ToDouble(_mean);
                for (int i = 0; i < _KichThuoc; i++)
                {
                    if (_DanhSach[i] != "?")
                    {
                        double t = Convert.ToDouble(_DanhSach[i]);
                        temp += (Math.Pow((t - mean), 2)) / (_KichThuoc - 1 - _SoMauThieu);
                    }
                }
                return Math.Sqrt(temp).ToString();
            }
            return null;
        }

        public void replace()
        {
            for (int i = 0; i < _KichThuoc; i++)
            {
                if (_DanhSach[i] == "?")
                    _DanhSach[i] = _GiaTri;
            }
            _SoMauThieu = 0;
        }

        public void Load()
        {
            if (_DanhSachPhanBiet != null)
                _DanhSachPhanBiet.Clear();
            _DanhSachPhanBiet = MauPhanBiet();
            _SoMauPhanBiet = _DanhSachPhanBiet.Count;
            _SoMauThieu = MauThieu();
            _GiaTri = GiaTriDaiDien();
            SortDanhSach();
            DemGiaTriPhanBiet();
            Fix_GiaTri();
        }

        void Fix_GiaTri()
        {
            if (_LoaiThuocTinh == "numeric")
            {
                _Min = _DanhSachPhanBiet[0];
                _Max = _DanhSachPhanBiet[_DanhSachPhanBiet.Count - 1];
                _mean = S_mean();
                _StdDev = S_StdDev();
            }
            else
            {
                _Min = null;
                _Max = null;
                _mean = null;
                _StdDev = null;
            }
        }

        List<string> _ChuanHoaMinMax(double min,double max)
        {
            List<string> temp = new List<string>();
            if(_LoaiThuocTinh=="numeric")
            {                
                for (int i = 0; i < _KichThuoc; i++)
                { 
                    double mn = Convert.ToDouble(_Min);
                    double mx = Convert.ToDouble(_Max);
                    double gt = Convert.ToDouble(_DanhSach[i]);
                    double tmp = ((gt-mn)/(mx-mn))*(max-min)+min;
                    temp.Add(tmp.ToString());
                }                
            }
            return temp;
        }

        public void ChuanHoaMinMax(double min, double max)
        {
            if (_LoaiThuocTinh == "numeric")
                _DanhSach = _ChuanHoaMinMax(min, max);
        }

        List<string> _ChuanHoaZScore()
        {
            List<string> temp = new List<string>();
            if (_LoaiThuocTinh == "numeric")
            {
                for (int i = 0; i < _KichThuoc; i++)
                {                   
                    double gt = Convert.ToDouble(_DanhSach[i]);
                    double mean = Convert.ToDouble(_mean);
                    double stddev = Convert.ToDouble(_StdDev);
                    double tmp = (gt - mean)/stddev;
                    temp.Add(tmp.ToString());
                }
            }
            return temp;
        }

        public void ChuanHoaZScore()
        {
            if (_LoaiThuocTinh == "numeric")
                _DanhSach = _ChuanHoaZScore();
        }

        public List<string> ChiaChieuRong(int n)
        {
            List<string> ds = new List<string>();
            if (_LoaiThuocTinh == "numeric" )
            {
                double min = Convert.ToDouble(_Min);
                double max = Convert.ToDouble(_Max);
                double kc = (max - min) / (n);

                for (int j = 0; j < n - 1; j++)
                {
                    ds.Add((min + kc).ToString());
                    min += kc;
                }
            }
            return ds;
        }

        public List<string> ChiaChieuSau(int n)
        {
            List<string> ds = new List<string>();

            if (n > _DemPhanBiet.Count)
                n = _DemPhanBiet.Count;
            else
                n--;

            if (_LoaiThuocTinh == "numeric")
            {
                int tyle = (int)(_KichThuoc / n);
                int flag = 0;
                for (int i = 0; i < _DemPhanBiet.Count; i++)
                {
                    int k = _DemPhanBiet[i];
                    if (ds.Count == n-1)
                    {
                        ds.Add(_DanhSachPhanBiet[i]);
                        return ds;
                    }

                    if (flag + k < tyle)
                    {
                        flag += k;
                    }
                    else
                    {
                        double t=0;
                        if (i >= 1)
                            t = (Convert.ToDouble(_DanhSachPhanBiet[i]) + Convert.ToDouble(_DanhSachPhanBiet[i - 1])) / 2;
                        else
                            t = Convert.ToDouble(_DanhSachPhanBiet[i]);
                        ds.Add(t.ToString());
                        flag = k;
                    }
                }
            }
            return ds;
        }

        public List<string> ThayGiaTriDaChia(List<string> ds)
        {
            List<string> temp = new List<string>();
            temp.AddRange(_DanhSach);            
            if (_LoaiThuocTinh == "numeric")
            {                
                int n = ds.Count;
                for (int i = 0; i < _KichThuoc; i++)
                {
                    double gt = Convert.ToDouble(temp[i]);
                    if (gt <= Convert.ToDouble(ds[0]))
                    {
                        temp[i] = "'(-inf-" + ds[0] + "]'";
                    }
                    else
                    {
                        if (gt > Convert.ToDouble(ds[n-1]))
                        {
                            temp[i] = "'(" + ds[n - 1] + "-inf)'";
                        }
                        else
                        {
                            for (int j = 0; j < n - 1; j++)
                            {
                                double a = Convert.ToDouble(ds[j]);
                                double b = Convert.ToDouble(ds[j+1]);
                                if (gt > a && gt <= b)
                                {
                                    temp[i] = "'(" + ds[j] + "-" + ds[j + 1] + "]'";
                                }
                            }
                        }
                    }
                }                
            }            
            return temp;
        }
        
        public void ChiaGio(int n, int loai)
        {
            if (_LoaiThuocTinh == "numeric")
            {
                if (loai == 0) //Chieu Rong
                {
                    List<string> temp = ChiaChieuRong(n);
                    _DanhSach =  ThayGiaTriDaChia(temp);
                    
                }
                else
                {
                    List<string> temp = ChiaChieuSau(n);
                    _DanhSach = ThayGiaTriDaChia(temp);                   
                }
                _LoaiThuocTinh = "nominal";
            }
        }

    }




    /*
     * LOP DANH SACH CAC DOI TUONG
     */ 

    public class DanhSach
    {
        List<DoiTuong> _danhsach;// = new List<DoiTuong>();

        public DanhSach(string url)
        {
            ReadCSV(url);
        }

        public List<DoiTuong> danhsach
        {
            get { return _danhsach; }
            set { _danhsach = value; }
        }

        int _SoLuongMau;
        Boolean _Miss;
        int _SoThuocTinh;

        public Boolean Miss
        {
            get { return _Miss; }
        }
        public int SoLuongMau
        {
            get 
            {
                _SoLuongMau = danhsach[0].DanhSach.Count;
                return _SoLuongMau; 
            }            
        }
        public int SoThuocTinh
        {
            get
            {
                _SoThuocTinh = danhsach.Count;
                return _SoThuocTinh;
            }  
        }

        string Check_LoaiThuocTinh(string str)
        {            
            if (str != "?")
            {
                try
                {
                    Convert.ToDouble(str);
                    return "numeric";
                }
                catch (FormatException ex)
                {
                    return "nominal";
                }
            }
            else
                return "?";
        }

        Boolean Set_LoaiThuocTinh(string[] temp)
        {
            Boolean kt = true;
            for (int i = 0; i < temp.Length; i++)
            {
                if (_danhsach[i].LoaiThuocTinh == null || _danhsach[i].LoaiThuocTinh == "" || _danhsach[i].LoaiThuocTinh == "?")
                {
                    string tmp = Check_LoaiThuocTinh(temp[i]);
                    if (tmp == "?")
                        kt = false;                    
                    _danhsach[i].LoaiThuocTinh = tmp;
                }
            }            
            return kt;
        }

        void Read_LoaiThuocTinh(string url)
        {
            var reader = new StreamReader(File.OpenRead(url));
            var line = reader.ReadLine();

            Boolean kt = true;
            do
            {                
                try
                {
                    line = reader.ReadLine();
                    var values = line.Split(',');
                    kt = Set_LoaiThuocTinh(values);
                }
                catch (Exception ex)
                {
                    line = reader.ReadLine();
                    var values = line.Split(';');
                    kt = Set_LoaiThuocTinh(values);
                }                
            }while(kt!=true);
        }

        void Read_TenThuocTinh(string url)
        {
            var reader = new StreamReader(File.OpenRead(url));
            var line = reader.ReadLine();
            try
            {
                var values = line.Split(',');
                _danhsach = new List<DoiTuong>(values.Length);
                for (int i = 0; i < values.Length; i++)
                {
                    //_danhsach[i].TenThuocTinh = values[i];                    
                    DoiTuong temp = new DoiTuong();
                    temp.TenThuocTinh = values[i];
                    _danhsach.Add(temp);
                }
            }
            catch (Exception ex)
            {
                var values = line.Split(';');
                _danhsach = new List<DoiTuong>(values.Length);
                for (int i = 0; i < values.Length; i++)
                {
                    DoiTuong temp = new DoiTuong();
                    temp.TenThuocTinh = values[i];
                    _danhsach.Add(temp);
                }
            }
        }

        void AddData(string[] values)
        {
            for (int i = 0; i < values.Length; i++)
            {
                _danhsach[i].Add(values[i]);
            }
        }

        void Read_Data(string url)
        {
            var reader = new StreamReader(File.OpenRead(url));
            var line = reader.ReadLine();

            while (!reader.EndOfStream)
            {
                line = reader.ReadLine();
                try
                {
                    var values = line.Split(',');
                    AddData(values);
                }
                catch (Exception ex)
                {
                    var values = line.Split(',');
                    AddData(values);
                }
            }
        }

        Boolean CheckMiss()
        {
            for (int i = 0; i < _danhsach.Count; i++)
                if (_danhsach[i].SoMauThieu != 0)
                    return true;
            return false;
        }

        public void Load()
        {
            for (int i = 0; i < _danhsach.Count; i++)
            {
                _danhsach[i].Load();                                          
            }
            _Miss = CheckMiss();
        }

        public void replace()
        {
            for (int i = 0; i < _danhsach.Count-1; i++)
            {
                _danhsach[i].replace();
            }
        }

        public void ReadCSV(string url)
        {
            Read_TenThuocTinh(url);
            Read_LoaiThuocTinh(url);
            Read_Data(url);
            Load();
        }

        public void ChuanMinMax(double min, double max)
        {
            for (int i = 0; i < _danhsach.Count-1; i++)
            {
                _danhsach[i].ChuanHoaMinMax(min,max);
            }
        }

        public void ChuanZScore()
        {
            for (int i = 0; i < _danhsach.Count-1; i++)
            {
                _danhsach[i].ChuanHoaZScore();
            }
        }

        public void ChiaGio(int n,int loai)
        {
            for (int i = 0; i < _danhsach.Count-1; i++)
            {
                _danhsach[i].ChiaGio(n, loai);
            }
        }

        void SaveFile_ARFF(string url)
        {
            StreamWriter sw = new StreamWriter(url);
            
            string r = url.Substring(url.LastIndexOf("\\")+1);

            sw.WriteLine("@relation "+r+"\n");
            
            for (int i = 0; i < _danhsach.Count; i++)
            {
                string ten = _danhsach[i].TenThuocTinh;
                //ten = ten.Replace(" ", "_");
                ten = ten.Replace(' ', '_');
                if (_danhsach[i].LoaiThuocTinh == "numeric")
                {
                    sw.WriteLine("@attribute " + ten + " " + _danhsach[i].LoaiThuocTinh);
                    sw.Flush();
                }
                else
                {                    
                    sw.Write("@attribute " + ten + " {");
                    for (int j = 0; j < _danhsach[i].SoMauPhanBiet; j++)
                    {
                        sw.Write(_danhsach[i].DanhSachMauPhanBiet[j]);
                        if (j != _danhsach[i].SoMauPhanBiet - 1)
                            sw.Write(",");
                    }
                    sw.Write("}\n");
                    sw.Flush();
                }
            }
            sw.WriteLine("\n@Data\n");
            int n = _danhsach[0].KichThuoc;
            for (int i = 0; i < n; i++)
            {
                for (int j = 0; j < _danhsach.Count; j++)
                {
                    sw.Write(_danhsach[j].DanhSach[i]);
                    if (j != danhsach.Count - 1)
                        sw.Write(",");
                }
                sw.WriteLine();
                sw.Flush();
            }
            sw.Close();
        }

        void SaveFile_CSV(string url)
        {
            StreamWriter sw = new StreamWriter(url);
            for (int i = 0; i < _danhsach.Count; i++)
            {
                sw.Write(danhsach[i].TenThuocTinh);
                if (i != danhsach.Count - 1)
                    sw.Write(",");
                sw.Flush();
            }
            sw.WriteLine();
            int n = _danhsach[0].KichThuoc;

            for (int i = 0; i < n; i++)
            {
                for (int j = 0; j < _danhsach.Count; j++)
                {
                    sw.Write(_danhsach[j].DanhSach[i]);
                    if (j != danhsach.Count - 1)
                        sw.Write(",");
                }
                sw.WriteLine();
                sw.Flush();
            }
            sw.Close();
        }

        public Boolean SaveFile(string url, int loai)
        {
            switch (loai)
            { 
                case 1:
                    SaveFile_CSV(url);
                    break;
                case 2:
                default:
                    SaveFile_ARFF(url);
                    break;                
            }
            return true;
        }

        public void ChiaChieuRong(int i,int n,ref List<string> ds,ref List<int> ts)
        {
            List<string> temp = danhsach[i].ChiaChieuRong(n);
            List<string> new_temp = danhsach[i].ThayGiaTriDaChia(temp);
            ds = danhsach[i].MauPhanBiet(new_temp);
            ts = danhsach[i].DemGiaTriPhanBiet(ds, new_temp);
            
        }

        public void ChiaChieuSau(int i, int n, ref List<string> ds, ref List<int> ts)
        {
            List<string> temp = danhsach[i].ChiaChieuSau(n);
            List<string> new_temp = danhsach[i].ThayGiaTriDaChia(temp);
            ds = danhsach[i].MauPhanBiet(new_temp);
            ts = danhsach[i].DemGiaTriPhanBiet(ds, new_temp);
        }
    }

}
